//
//  SPMapView.m
//  SPMapKit
//
//  Created by zhangbo on 2017/9/8.
//  Copyright © 2017年 zhangbo. All rights reserved.
//

#import "SPMapView.h"
#import "SPDefaultAnnotation.h"
#import "BingMapTileProvider.h"
#import "GoogleMapTileProvider.h"

@interface SPMapView()
{
    UITapGestureRecognizer *_singleTap;
    UITapGestureRecognizer *_doubleTap;
}

@end

@implementation SPMapView

-(id) initWithFrame:(CGRect)frame
{
    self = [self initWithFrame:frame contentSize:CGSizeMake(256, 256)];
    if (self) {
        self.zoomScale = 2;
        self.maxLevelsOfZoom = 19;
    }
    
    return self;
}

- (id)initWithFrame:(CGRect)frame contentSize:(CGSize)contentSize
{
    self = [super initWithFrame:frame];
    if (self) {
        _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
        _scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
        _scrollView.delegate = self;
        _scrollView.contentSize = contentSize;
        _scrollView.bouncesZoom = YES;
        _scrollView.bounces = YES;
        _scrollView.minimumZoomScale = 2.0;
        _scrollView.showsVerticalScrollIndicator = NO;
        _scrollView.showsHorizontalScrollIndicator = NO;
        self.mapType = SPMapTypeStandard;
        self.mapProvider = GOOGLEMAP_PROVIDER;
        
        _imgDic = [[NSMutableDictionary alloc] init];
        
        _canvasView = [[SPCanvasView alloc] initWithFrame:CGRectMake(0, 0, _scrollView.frame.size.width, _scrollView.frame.size.height)];
        _canvasView.userInteractionEnabled = NO;
        _canvasView.delegate = self;
        
        CGRect canvas_frame = CGRectMake(0.0f, 0.0f, _scrollView.contentSize.width, _scrollView.contentSize.height);
        
        _tiledView = [[SPTiledView alloc] initWithFrame:canvas_frame];
        _tiledView.delegate = self;
        
        [_scrollView addSubview:_tiledView];
        
        _rulerView = [[ScaleRulerView alloc] initWithFrame:CGRectMake(15, 50, 250, 20)];
        [_rulerView show:NO animated:NO];
        
        [self addSubview:_scrollView];
        [self addSubview:_canvasView];
        [self addSubview:_rulerView];
        
        _singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapReceived:)];
        _singleTap.numberOfTapsRequired = 1;
        _singleTap.delegate = self;
        [_tiledView addGestureRecognizer:_singleTap];
        
        _doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapReceived:)];
        _doubleTap.numberOfTapsRequired = 2;
        _doubleTap.delegate = self;
        [_tiledView addGestureRecognizer:_doubleTap];
    }
    
    return self;
}

-(void) setMapProvider:(SPMapPorvider)mapProvider
{
    _mapProvider = mapProvider;
    switch (mapProvider) {
        case BINGMAP_PROVIDER:
            _provider = [[BingMapTileProvider alloc] init];
            if (self.mapType == SPMapTypeStandard) {
                self.maxLevelsOfZoom = 18;
            }else{
                self.maxLevelsOfZoom = 19;
            }
            break;
        case GOOGLEMAP_PROVIDER:
            _provider = [[GoogleMapTileProvider alloc] init];
            break;
            
        default:
            _provider = [[BingMapTileProvider alloc] init];
            break;
    }
}

#pragma mark - SPTiledImageDelegate
-(UIImage *)tiledView:(SPTiledView *)tiledView imageForRow:(NSInteger)row column:(NSInteger)column scale:(NSInteger)scale
{
    // 白板地图直接返回
    if (_mapType == SPMapTypeBlank) {
        return [self imageWithColor:[UIColor whiteColor]];
    }
    
    if (_provider)
    {
        int level = MAX(0, log2f(scale));
        
        NSURL *url = [NSURL URLWithString:[_provider tileImageUrl:row col:column level:level mapType:self.mapType]];
        NSLog(@"%@", url.absoluteString);
        
        /********************** 新增 ********************/
        MapImageInfo *tempinfo = [[MapImageInfo alloc]init];
        tempinfo.row = row;
        tempinfo.column = column;
        tempinfo.level = level;
        tempinfo.urlStr = url.absoluteString;
        tempinfo = [[SPMapDataManager Instance] getImageForMap:tempinfo];
        if (tempinfo.image)
        {
            return tempinfo.image;
        }
        //设置默认图片
        else
        {
            if (_mapType == SPMapTypeStandard)
            {
                return [UIImage imageNamed:@"default_white_bg"];
            }
            else
            {
                return [UIImage imageNamed:@"default_dark_bg"];
            }
        }
        
    }
    
    return nil;
}

#pragma mark - UIScrollViewDelegate

-(void) setMapType:(SPMapType)mapType
{
    _mapType = mapType;
    self.maxLevelsOfZoom = 19;
    if (mapType == SPMapTypeStandard) {
        _rulerView.scaleStyle = DARK_STYLE;
        _scrollView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"default_white_bg"]];
        if (self.mapProvider == BINGMAP_PROVIDER) {
            self.maxLevelsOfZoom = 18;
        }
    }else if(mapType == SPMapTypeBlank){
        _rulerView.scaleStyle = DARK_STYLE;
        _scrollView.backgroundColor = [UIColor whiteColor];
    }else{
        _rulerView.scaleStyle = LIGHT_STYLE;
        _scrollView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"default_dark_bg"]];
    }
    [_tiledView setNeedsDisplay];
    [self locate:_centerGPS animated:NO];
}

-(CGFloat) zoomScale
{
    return _scrollView.zoomScale;
}

-(void) setZoomScale:(CGFloat)zoomScale
{
    [_scrollView setZoomScale:zoomScale animated:NO];
}

- (void)setZoomScale:(CGFloat)zoomScale animated:(BOOL)animated
{
    [_scrollView setZoomScale:zoomScale animated:animated];
}

-(void) setMaxLevelsOfZoom:(int)maxLevelsOfZoom
{
    _maxLevelsOfZoom = maxLevelsOfZoom;
    _scrollView.maximumZoomScale = pow(2.0, MAX(0.0f, maxLevelsOfZoom));
    _tiledView.numberOfZoomLevels = maxLevelsOfZoom;
}

-(void) setLevelsOfZoom:(int)levelsOfZoom
{
    _levelsOfZoom = levelsOfZoom;
    _scrollView.zoomScale = pow(2, levelsOfZoom);
}

-(UIView*) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return _tiledView;
}

-(void) scrollViewDidZoom:(UIScrollView *)scrollView
{
    int level = MAX(0, log2f(_scrollView.zoomScale));
    _levelsOfZoom = level;
    _rulerView.groundResolution = [_provider GroundResolution:_centerGPS.latitude level:level];
    _rulerView.zoomScale = _scrollView.zoomScale;
    [_rulerView setNeedsDisplay];
    _canvasView.zoomScale = scrollView.zoomScale;
    [_canvasView refresh];
}

-(void) scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
    [_rulerView show:YES animated:YES];
}

-(void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
{
    [_rulerView show:NO animated:YES];
}

-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
    _canvasView.contentOffset = scrollView.contentOffset;
    CGPoint offset = CGPointMake(scrollView.contentOffset.x + scrollView.frame.size.width / 2, scrollView.contentOffset.y + scrollView.frame.size.height / 2);
    CGFloat rate = scrollView.zoomScale / pow(2, _levelsOfZoom);
    offset.x /= rate;
    offset.y /= rate;
    _centerGPS = [_provider ContentOffsetToGPS:offset level:_levelsOfZoom mapType:_mapType];
//    NSLog(@"centerX:%f   centerY:%f", _centerGPS.latitude, _centerGPS.longitude);
    [_canvasView refresh];
}

-(void) setCenterGPS:(CLLocationCoordinate2D)centerGPS
{
    [self locate:centerGPS animated:YES];
}

-(void) locate:(CLLocationCoordinate2D) gps level:(int) level
{
    _levelsOfZoom = level;
    _scrollView.zoomScale = pow(2, level);
    CGPoint offset = [_provider GPSToContentOffset:gps level:level mapType:_mapType];
    CGSize screensize = self.frame.size;
    offset.x -= screensize.width / 2.0;
    offset.y -= screensize.height / 2.0;
    
//    _scrollView.contentOffset = offset;
    [_scrollView setContentOffset:offset animated:YES];
}

-(void) locate:(CLLocationCoordinate2D) gps animated:(BOOL) animated
{
    CGPoint offset = [_provider GPSToContentOffset:gps level:_levelsOfZoom mapType:_mapType];
    float rate = _scrollView.zoomScale / pow(2, _levelsOfZoom);
    offset.x *= rate;
    offset.y *= rate;
    CGSize screensize = self.frame.size;
    offset.x -= screensize.width / 2.0;
    offset.y -= screensize.height / 2.0;
    
    //    _scrollView.contentOffset = offset;
    [_scrollView setContentOffset:offset animated:animated];
}

-(void) locate:(CLLocationCoordinate2D) gps offset:(CGPoint) offsetpoint
{
    CGPoint offset = [_provider GPSToContentOffset:gps level:_levelsOfZoom mapType:_mapType];
    float rate = _scrollView.zoomScale / pow(2, _levelsOfZoom);
    offset.x *= rate;
    offset.y *= rate;
    CGSize screensize = self.frame.size;
    offset.x -= (screensize.width / 2.0 + offsetpoint.x);
    offset.y -= (screensize.height / 2.0 + offsetpoint.y);
    
    [_scrollView setContentOffset:offset animated:YES];
}

-(void) locateToRegion:(MKCoordinateRegion)region animated:(BOOL) animated
{
    CGFloat scaleX,scaleY;
    
    CLLocationCoordinate2D center = region.center;
    MKCoordinateSpan span = region.span;
    CGFloat disLat = [self calcDistance:center.latitude - span.latitudeDelta lng1:center.longitude lat2:center.latitude + span.latitudeDelta lng2:center.longitude];
    CGFloat latRS = disLat / self.frame.size.height;
    
    CGFloat rs = 0.0001;
    int i=0;
    for (i=19; i>0; i--) {
        CGFloat temp = [_provider GroundResolution:center.latitude level:i];
        if (latRS > temp) {
            rs = temp;
        }else{
            break;
        }
    }
    
    scaleX = pow(2, i) * latRS / rs;
    
    CGFloat disLon = [self calcDistance:center.latitude lng1:center.longitude - span.longitudeDelta lat2:center.latitude lng2:center.longitude + span.longitudeDelta];
    CGFloat lonRS = disLon / self.frame.size.width;
    
    rs = 0.0001;
    for (i=19; i>0; i--) {
        CGFloat temp = [_provider GroundResolution:center.latitude level:i];
        if (latRS > temp) {
            rs = temp;
        }else{
            break;
        }
    }
    
    scaleY = pow(2, i) * lonRS / rs;
    
    _scrollView.zoomScale = MIN(scaleX, scaleY);
    [self locate:center animated:animated];
}

-(void) addAnnotation:(id<SPAnnotation>) annotation
{
    if (![_canvasView.annotations containsObject:annotation]) {
        [_canvasView addAnnotation:annotation];
    }
    
    [_canvasView refresh];
}

-(void) removeAnnotation:(id<SPAnnotation>) annotation
{
    [_canvasView removeAnnotation:annotation];
}

-(void) removeAllAnnotations
{
    [_canvasView removeAllAnnotations];
}

-(void) addOverlay:(id<SPOverlay>) overlay
{
    [_canvasView addOverlay:overlay];
    [self refreshOverlay];
}

-(void) removeOverlay:(id<SPOverlay>) overlay
{
    [_canvasView removeOverlay:overlay];
    [self refreshOverlay];
}

-(void) removeALLOverlays
{
    [_canvasView removeALLOverlays];
    [self refreshOverlay];
}

-(NSMutableArray*) annotations
{
    return [_canvasView annotations];
}

-(NSMutableArray*) overlays
{
    return [_canvasView overlays];
}

-(void) refresh
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [_canvasView refresh];
    });
    
}

-(void) refresh:(id<SPAnnotation>) annotation
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [_canvasView refresh:annotation];
    });
}

-(void) refreshOverlay
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [_canvasView refreshOverlay];
    });
}

#pragma mark - SPCanvasDelegate
-(SPAnnotationView*) viewForAnnotation:(id <SPAnnotation>)annotation
{
    if (self.delegate) {
        return [self.delegate mapView:self viewForAnnotation:annotation];
    }
    
    return nil;
}

- (SPOverlayRenderer *) rendererForOverlay:(id <SPOverlay>)overlay
{
    if (self.delegate) {
        SPOverlayRenderer *render = [self.delegate mapView:self rendererForOverlay:overlay];
        render.delegate = self;
        return render;
    }
    
    return nil;
}

-(CGPoint) GPSToScreenPosition:(CLLocationCoordinate2D) gpspoint
{
//    int level = MAX(0, log2f(_scrollView.zoomScale));
    
    CGPoint offset = [_provider GPSToContentOffset:gpspoint level:18 mapType:_mapType];
    CGPoint position;
    double levelScale = pow(2, 18);
    position.x = offset.x * _scrollView.zoomScale / levelScale - _scrollView.contentOffset.x;
    position.y = offset.y * _scrollView.zoomScale / levelScale - _scrollView.contentOffset.y;
    
    return position;
}

-(CGFloat) GroundResolution:(CGFloat) lat
{
    int level = MAX(0, log2f(_scrollView.zoomScale));
    CGFloat gr = [_provider GroundResolution:lat level:level];
    return gr / _scrollView.zoomScale * pow(2, level);
}

#pragma mark - 
- (void)singleTapReceived:(UITapGestureRecognizer *)gestureRecognizer
{
//    NSLog(@"singleTapReceived");
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
//    NSLog(@"gestureRecognizerShouldBegin");
    CGPoint location = [gestureRecognizer locationInView:_canvasView];
    
    if (gestureRecognizer == _singleTap) {
        for (id<SPAnnotation> annotation in [self annotations]) {
            SPAnnotationView *view = [_canvasView getAnnotatioinView:annotation];
            if (CGRectContainsPoint([view visibleFrame], location))
            {
//                NSLog(@"clicked view!!!!");
                if (!view.canTouch) {
                    continue;
                }
                
                if (self.delegate && [self.delegate respondsToSelector:@selector(mapView:didSelectAnnotationView:)])
                {
                    [self.delegate mapView:self didSelectAnnotationView:view];
                }
                return NO;
            }
        }
        
        for (id<SPOverlay> overlay in [self overlays]) {
            // 暂时只判断直线的点击事件
            if ([overlay isMemberOfClass:[SPSingleLine class]]) {
                NSArray *pointList = [_canvasView getOverlayScreenPointList:overlay];
                if ([pointList count] < 2) {
                    return YES;
                }
                
                CGPoint p0 = ((NSValue*)[pointList firstObject]).CGPointValue;
                CGPoint p1 = ((NSValue*)[pointList objectAtIndex:1]).CGPointValue;
                
                double distance = pointToSegDist(location.x, location.y, p0.x, p0.y, p1.x, p1.y);
//                NSLog(@"distance:  %f", distance);
                if (distance < 10) {
//                    NSLog(@"clicked line!!!!");
                    if (self.delegate && [self.delegate respondsToSelector:@selector(mapView:didSelectOverlay:)])
                    {
                        [self.delegate mapView:self didSelectOverlay:overlay];
                    }
                    return YES;
                }
            }
        }
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(mapView:didSelectBackground:)])
        {
            // 点击位置转换为gps坐标，待加
            [self.delegate mapView:self didSelectBackground:[self pointToGPS:location]];
        }
    }
    
    if (gestureRecognizer == _doubleTap) {
        if (_levelsOfZoom < self.maxLevelsOfZoom) {
            [_scrollView setZoomScale:_scrollView.zoomScale * 2 animated:YES];
        }
    }
    
    return YES;
}

-(CLLocationCoordinate2D) pointToGPS:(CGPoint) point
{
    CGPoint offset = CGPointMake(_scrollView.contentOffset.x + point.x, _scrollView.contentOffset.y + point.y);
    CGFloat rate = _scrollView.zoomScale / pow(2, _levelsOfZoom);
    offset.x /= rate;
    offset.y /= rate;
    
    return [_provider ContentOffsetToGPS:offset level:_levelsOfZoom mapType:_mapType];
}

#pragma mark - other

- (UIImage*) imageWithColor:(UIColor*)color
{
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);
    
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return image;
}

-(double) calcDistance:(double) lat1 lng1:(double) lng1 lat2:(double) lat2 lng2:(double) lng2
{
    return distanceRadians(toRadians(lat1), toRadians(lng1), toRadians(lat2), toRadians(lng2)) * EarthRadius;
}

double toRadians(double angdeg) {
    return angdeg / 180.0 * M_PI;
}

double arcHav(double x)
{
    return 2 * asin(sqrt(x));
}

double hav(double x) {
    double sinHalf = sin(x * 0.5);
    return sinHalf * sinHalf;
}

double havDistance(double lat1, double lat2, double dLng) {
    return hav(lat1 - lat2) + hav(dLng) * cos(lat1) * cos(lat2);
}

double distanceRadians(double lat1, double lng1, double lat2, double lng2) {
    return arcHav(havDistance(lat1, lat2, lng1 - lng2));
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

@end
